#include "glwindow.h"

GLWindow::GLWindow(QWindow* parent): QWindow(parent),
    m_pContext(Q_NULLPTR)
{
    setSurfaceType(OpenGLSurface);
    resize(800, 450);
}

/*------------------------------------------------------------------------------*/

GLuint g_VertexShader = 0;
GLuint g_FragmentShader = 0;
GLuint g_Program = 0;

GLuint g_Buffer = 0;

GLuint g_VertexArray = 0;
GLuint g_TexCoord = 0;

GLuint tex_image = 0;
GLuint tex_color = 0;

struct vglImageMipData {
    GLsizei width;
    GLsizei height;
    GLsizei depth;
    GLsizeiptr mipStride;
    GLvoid* data;
};

void GLWindow::addSprite(KSprite *sp){
    sprites.append(sp);
}

inline QByteArray ShaderData( const QString& fileName )
{
   QString path = "E:/QtCreatorProjects/QtOpenGLDemo/";
   QString filePath = path.append(fileName);
   QByteArray result;
   QFile shaderFile(filePath);
   if ( shaderFile.open( QIODevice::ReadOnly ) )
   {
       result = shaderFile.readAll( );
       shaderFile.close( );
   }
   return result;
}

void GLWindow::exposeEvent( QExposeEvent* pEvent )
{
   Q_UNUSED( pEvent );

   if ( m_pContext == Q_NULLPTR )
   {
       QSurfaceFormat format;
       format.setVersion( 4, 3 );      // 需要OpenGL4.3版本
       format.setSamples( 4 );         // 四倍采样

       m_pContext = new QOpenGLContext( this );
       m_pContext->setFormat( format );
       m_pContext->create();
       m_pContext->makeCurrent( this );

       initializeOpenGLFunctions();
       InitGL();
   }
   ResizeGL( width(), height() );

   RenderGL();
   m_pContext->swapBuffers( this );
}


/*---------------------------------------------------------------------------*/
void GLWindow::InitGL( void )
{
    LoadShaders("Shader.vert", "Shader.frag");
    sprite = new KSprite("E:/QtCreatorProjects/QtOpenGLDemo/disruptor.png");
    sprite->setPosition(500, 250);
    addSprite(sprite);

    tex_image = sprite->texId;
    imgWidth = sprite->width;
    imgHeight = sprite->height;

    initVertexData();
    glViewport( width() - viewportWidth, height() - viewportHeight, width(), height());

    glClearColor(0.f, 0.f, 1.f, 1.f);
    glPointSize( 10.0f );
}

void GLWindow::LoadShaders(const QString& vertShader, const QString& fragShader ) {
    // 初始化顶点着色器
    QByteArray vsData = ShaderData( vertShader );
    const char* vsPointer = vsData.constData( );
    g_VertexShader = glCreateShader( GL_VERTEX_SHADER );
    glShaderSource( g_VertexShader,                             // 着色器标识
                    1,                                          // 着色器文本数目
                    &vsPointer,                                 // 着色器字符串指针
                    Q_NULLPTR );                                // 字符串长度
    glCompileShader( g_VertexShader );

    // 初始化片断着色器
    QByteArray fsData = ShaderData( fragShader );
    const char* fsPointer = fsData.constData( );
    g_FragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
    glShaderSource( g_FragmentShader,                           // 着色器标识
                    1,                                          // 着色器文本数目
                    &fsPointer,                                 // 着色器字符串
                    Q_NULLPTR );                                // 字符串长度
    glCompileShader( g_FragmentShader );

    // 初始化着色器程序
    g_Program = glCreateProgram( );
    glAttachShader( g_Program, g_VertexShader );
    glAttachShader( g_Program, g_FragmentShader );
    glLinkProgram( g_Program );

    // 删除着色器
    glDeleteShader( g_VertexShader );
    glDeleteShader( g_FragmentShader );
}

void GLWindow::initVertexData() {
    GLfloat sw = sprite->width;
    GLfloat sh = sprite->height;

    GLfloat x = sprite->posX;
    GLfloat y = sprite->posY;
    GLfloat dw = designWidth;
    GLfloat dh = designHeight;

    // 初始化顶点数组对象（VAO）
    GLfloat vertices[] = {
        //顶点坐标
        (2*x-sw-dw)/dw, (2*y-sh-dh)/dh,
        (2*x+sw-dw)/dw, (2*y-sh-dh)/dh,
        (2*x-sw-dw)/dw, (2*y+sh-dh)/dh,
        (2*x+sw-dw)/dw, (2*y+sh-dh)/dh,
        (2*x-sw-dw)/dw, (2*y+sh-dh)/dh,
        (2*x+sw-dw)/dw, (2*y-sh-dh)/dh,
        //纹理坐标
        0.f, 0.f,
        1.f, 0.f,
        0.f, 1.f,
        1.f, 1.f,
        0.f, 1.f,
        1.f, 0.f
    };

    glGenVertexArrays( 1, &g_VertexArray );
    glBindVertexArray( g_VertexArray );

    glGenBuffers(1, &g_Buffer);
    glBindBuffer(GL_ARRAY_BUFFER, g_Buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(12 * sizeof(GLfloat)));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

}

/*---------------------------------------------------------------------------*/
void GLWindow::ResizeGL( int width, int height )
{
    if(width/viewportWidth > height/viewportHeight) {
        viewportWidth = viewportWidth * height/viewportHeight;
        viewportHeight = height;
    } else {
        viewportHeight = viewportHeight * width/viewportWidth;
        viewportWidth = width;
    }
    glViewport( (width - viewportWidth)/2, (height - viewportHeight)/2, viewportWidth, viewportHeight);
}
/*---------------------------------------------------------------------------*/
void GLWindow::RenderGL( void )
{
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram( g_Program );
    glBindVertexArray(g_VertexArray);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex_image);

    glDrawArrays(GL_TRIANGLES, 0, 3 * 2);
//    glFlush();
}

void GLWindow::ReleaseGL( void )
{
   glDeleteVertexArrays( 1, &g_VertexArray );
   glDeleteProgram( g_Program );
}

void GLWindow::loadTextures()
{
    static const GLubyte texCheckboardData[] = {
        0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
        0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
        0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
        0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
        0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
        0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
        0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
        0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff
    };

    GLubyte texCheckboardData2[256];
    for(int i = 0; i < 16; i++) {
        for(int j = 0; j < 16; j++) {
            if((i % 2 == 0 && j % 2 == 0) ||
                    (i % 2 == 1 && j % 2 == 1)) {
                texCheckboardData2[i*16+j] = 0xff;
            } else {
                texCheckboardData2[i*16+j] = 0x00;
            }
        }
    }

    static const GLfloat texColorData[] = {
        1.0f, 0.f, 0.f, 1.0f, 0.f, 1.0f, 0.f, 1.0f,
        0.f, 0.f, 1.0f, 1.0f, 1.0f, 1.0f, 0.f, 1.0f
    };

    glGenTextures(1, &tex_image);
    glBindTexture(GL_TEXTURE_2D, tex_image);
    glTexStorage2D(GL_TEXTURE_2D, 4, GL_R8, 16, 16);
    glTexSubImage2D(GL_TEXTURE_2D,
                    0,
                    0,0,
                    16,16,
                    GL_RED, GL_UNSIGNED_BYTE,
                    texCheckboardData2);

    glGenTextures(1, &tex_color);
    glBindTexture(GL_TEXTURE_2D, tex_color);
    glTexStorage2D(GL_TEXTURE_2D, 2, GL_RGBA32F, 2, 2);
    glTexSubImage2D(GL_TEXTURE_2D,
                    0,
                    0,0,
                    2,2,
                    GL_RGBA, GL_FLOAT,
                    texColorData);
}

GLuint GLWindow::readImage(const char *filename)
{
    GLuint tex_id;
    GLint alignment;

    QImage tex, buf;
    buf.load(filename);

    tex = QGLWidget::convertToGLFormat( buf );

    imgWidth = (GLfloat) tex.width();
    imgHeight = (GLfloat) tex.height();

    glGenTextures(1, &tex_id);
    glBindTexture( GL_TEXTURE_2D, tex_id );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);//??
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);//??

    glTexImage2D(GL_TEXTURE_2D,
                0, GL_RGBA,
                tex.width(), tex.height(),
                0,                          //border
                GL_RGBA, GL_UNSIGNED_BYTE,
                tex.bits());
    glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);

    return tex_id;
}

GLWindow::~GLWindow()
{

}

